package br.com.rookgraph;

import java.util.Collection;
import java.util.HashSet;

public class Swap {
	
	private Graph tree;
	
	private Graph originalGraph;
	
	private Edge cut;
	
	private Edge replace;

	private Integer swapBeta;
	private Integer swapGama;
	private Integer swapSigma;
	private Integer swapSigmaLinha;
	
	public boolean consideraArticulacoes = false;
	
	public Swap(Edge cut, Edge replace, Graph tree) {
		super();
		this.cut = cut;
		this.replace = replace;
		this.tree = tree;
		inicializaParametros();
	}
	
	public Swap(Edge cut, Edge replace) {
		super();
		this.cut = cut;
		this.replace = replace;
		inicializaParametros();
	}

	private void inicializaParametros() {
		swapBeta = null;
		swapGama = null;
		swapSigma = null;
		swapSigmaLinha = null;
	}
	
	public Graph getTree() {
		return tree;
	}
	
	public void setTree(Graph tree) {
		this.tree = tree;
	}

	public Graph getOriginalGraph() {
		return originalGraph;
	}
	
	public void setOrignalGraph(Graph graph) {
		this.originalGraph = graph;
	}

	public Edge getCut() {
		return cut;
	}

	public void setCut(Edge cut) {
		this.cut = cut;
	}

	public Edge getReplace() {
		return replace;
	}

	public void setReplace(Edge replace) {
		this.replace = replace;
	}
	
	public int diffAlfa(){
		return replace.alfa - cut.alfa;
	}
	
	public int diffGama(){
		return replace.gama - cut.gama;
	}
	
	public int swapBeta() {
		if (swapBeta == null) {
			Collection<Integer> listaDeVertices = getListaDeVertices(false);
			
			/* Alfa dos vértices antes da troca */
			int alfaBefore = 0;
			for (Integer v : listaDeVertices) {
				alfaBefore += this.tree.grau(v) > 2 ? 1 : 0; 
			}
			
			/* Faz a troca */
			this.tree.swapEdge(this);
			
			/* Alfa dos vértices antes da troca */
			int alfaAfter = 0;
			for (Integer v : listaDeVertices) {
				alfaAfter += this.tree.grau(v) > 2 ? 1 : 0; 
			}
			
			/* Desfaz a troca */
			this.tree.undoSwap(this);
			
			swapBeta = alfaAfter - alfaBefore;
		}
		
		return swapBeta;
	}

	public int swapGama() {
		if (swapGama == null) {
			Collection<Integer> listaDeVertices = getListaDeVertices(true);
			int limiar = 3;
			
			if (listaDeVertices.size() == 0) {
				return 999;
			}
			
			int gamaBefore = this.tree.grau(listaDeVertices.iterator().next());
			gamaBefore = getGamaDoVertice(listaDeVertices, limiar, gamaBefore);
	
			/* Faz a troca */
			this.tree.swapEdge(this);
			
			int gamaAfter = this.tree.grau(listaDeVertices.iterator().next());
			gamaAfter = getGamaDoVertice(listaDeVertices, limiar, gamaAfter);
			
			/* Desfaz a troca */
			this.tree.undoSwap(this);
			
			swapGama = gamaAfter - gamaBefore;
		}
		
		return swapGama;
	}

	private int getGamaDoVertice(Collection<Integer> listaDeVertices, int limiar, int gama) {
		for (Integer v : listaDeVertices) {
			int grauV = this.tree.grau(v);
			
			if (grauV >= limiar && gama >= limiar) {
				gama = Math.min(gama, grauV);
			} else {
				gama = Math.max(gama, grauV);
			}
		}
		
		return gama;
	}

	public int swapSigma() {
		if (swapSigma == null) {
			/* Sigma da aresta cut */
			int sigmaBefore = this.tree.grau(this.getCut().origin) + this.tree.grau(this.getCut().target) - 2;
			
			/* Faz a troca */
			this.tree.swapEdge(this);
			
			/* Sigma da aresta rep */
			int sigmaAfter = this.tree.grau(this.getReplace().origin) + this.tree.grau(this.getReplace().target) - 2;
			
			/* Desfaz a troca */
			this.tree.undoSwap(this);
			
			swapSigma = sigmaAfter - sigmaBefore;
				
		}
		
		return swapSigma;
	}

	public int swapSigmaLinha() {
		if (swapSigmaLinha == null) {
			/* Sigma linha da aresta cut */
			int sigmaLineBefore = this.originalGraph.grau(this.getCut().origin) + this.originalGraph.grau(this.getCut().target) - 2;
			
			/* Sigma linha da aresta rep */
			int sigmaLineAfter = this.originalGraph.grau(this.getReplace().origin) + this.originalGraph.grau(this.getReplace().target) - 2;
			
			swapSigmaLinha = sigmaLineAfter - sigmaLineBefore;
 	
//			swapSigmaLinha = sigmaLineBefore - sigmaLineAfter;
			

		}
		
		return swapSigmaLinha;
	}
	
	public Collection<Integer> getListaDeVertices(boolean consideraArticulacoes) {
		HashSet<Integer> vertices = new HashSet<Integer>();
		
		int cutOrigin = this.getCut().origin;
		int cutTarget = this.getCut().target;
		int repOrigin = this.getReplace().origin;
		int repTarget = this.getReplace().target;
		
		if (this.consideraArticulacoes && consideraArticulacoes) {
			int ARTICULACAO = 1;

			if (originalGraph.articulacoes[cutOrigin] != ARTICULACAO) vertices.add(new Integer(cutOrigin));
			if (originalGraph.articulacoes[cutTarget] != ARTICULACAO) vertices.add(new Integer(cutTarget));
			if (originalGraph.articulacoes[repOrigin] != ARTICULACAO) vertices.add(new Integer(repOrigin));
			if (originalGraph.articulacoes[repTarget] != ARTICULACAO) vertices.add(new Integer(repTarget));
		
//			if (!(originalGraph.articulacoes[cutOrigin] == ARTICULACAO && tree.grau(cutOrigin) > 3)) vertices.add(new Integer(cutOrigin));
//			if (!(originalGraph.articulacoes[cutTarget] == ARTICULACAO && tree.grau(cutTarget) > 3)) vertices.add(new Integer(cutTarget));
//			if (!(originalGraph.articulacoes[repOrigin] == ARTICULACAO && tree.grau(repOrigin) > 3)) vertices.add(new Integer(repOrigin));
//			if (!(originalGraph.articulacoes[repTarget] == ARTICULACAO && tree.grau(repTarget) > 3)) vertices.add(new Integer(repTarget));
		} else {
			vertices.add(new Integer(cutOrigin));
			vertices.add(new Integer(cutTarget));
			
			vertices.add(new Integer(repOrigin));
			vertices.add(new Integer(repTarget));			
		}
		
		return vertices;
	}
	
	@Override
	public boolean equals(Object other) {
		if(other instanceof Swap){
			Swap otherEdge = (Swap) other;
			return this.cut.equals(otherEdge.cut) && this.replace.equals(otherEdge.replace); 
		}
		return false; 
	}
	
	@Override
	public String toString() {
		return cut +" <------> "+ replace;
	}

	@Override
	public Object clone() throws CloneNotSupportedException {
		return new Swap(this.cut, this.replace);
	}
	
}
